// SPDX-License-Identifier: GPL-2.0
/**
 * motorcomm.c - Driver for MotorComm PHYs
 *
 * Copyright (C) 2016-2018, LomboTech Co.Ltd.
 * Author: lomboswer <lomboswer@lombotech.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <linux/module.h>
#include <linux/phy.h>

#define MII_DEBUG_REG_ADDR	0x1E	/* Debug register's address */
#define MII_DEBUG_REG_DATA	0x1F	/* Debug register's data */

#define EXTENED_LED0_CONTROL	0x40C0

#define LED_HT_ON_EN		BIT(5)
#define LED_ACT_BLK_IND		BIT(12)

static int motorcomm_phy_soft_reset(struct phy_device *phydev)
{
	int val;

	/* Set bit exREG_0x0050[6] to select clock input mode:
	 * MAC -> 50M -> PHY (without external crystal)
	 */
	val = phy_write(phydev, MII_DEBUG_REG_ADDR, 0x50);
	if (val < 0)
		return val;

	val = phy_read(phydev, MII_DEBUG_REG_DATA);
	if (val < 0)
		return val;

	val = phy_write(phydev, MII_DEBUG_REG_DATA, val | BIT(6));
	if (val < 0)
		return val;

	val = phy_write(phydev, MII_DEBUG_REG_ADDR, EXTENED_LED0_CONTROL);
	if (val < 0)
		return val;

	val = phy_read(phydev, MII_DEBUG_REG_DATA);
	if (val < 0)
		return val;

	val = phy_write(phydev, MII_DEBUG_REG_DATA, val | LED_HT_ON_EN | LED_ACT_BLK_IND);
	if (val < 0)
		return val;

	return genphy_soft_reset(phydev);
}

static struct phy_driver motorcomm_phy_driver = {
	.phy_id		= 0x00000128,
	.phy_id_mask	= 0xffffffff,
	.name		= "MotorComm PHY",
	.soft_reset	= motorcomm_phy_soft_reset,
	.config_init	= genphy_config_init,
	.features	= PHY_BASIC_FEATURES | SUPPORTED_MII |
			  SUPPORTED_AUI | SUPPORTED_FIBRE |
			  SUPPORTED_BNC,
	.aneg_done	= genphy_aneg_done,
	.suspend	= genphy_suspend,
	.resume		= genphy_resume,
	.set_loopback   = genphy_loopback,
};

static int __init motorcomm_phy_init(void)
{
	return phy_driver_register(&motorcomm_phy_driver, THIS_MODULE);
}

static void __exit motorcomm_phy_exit(void)
{
	phy_driver_unregister(&motorcomm_phy_driver);
}

module_init(motorcomm_phy_init);
module_exit(motorcomm_phy_exit);

MODULE_DESCRIPTION("Driver for MotorComm PHYs");
MODULE_AUTHOR("lomboswer <lomboswer@lombotech.com>");
MODULE_LICENSE("GPL v2");
